// GamerzFinest Public Aimbot By Bmthrules Using My Aimbot Base
if SERVER then return end

local BM = {}

local concommand = concommand
local cvars = cvars
local debug = debug
local ents = ents
local file = file
local hook = hook
local math = math
local spawnmenu = spawnmenu
local string = string
local surface = surface
local table = table
local timer = timer
local util = util
local vgui = vgui

local Angle = Angle
local CreateClientConVar = CreateClientConVar
local CurTime = CurTime
local ErrorNoHalt = ErrorNoHalt
local FrameTime = FrameTime
local GetConVarString = GetConVarString
local GetViewEntity = GetViewEntity
local include = include
local ipairs = ipairs
local LocalPlayer = LocalPlayer
local pairs = pairs
local pcall = pcall
local print = print
local RunConsoleCommand = RunConsoleCommand
local ScrH = ScrH
local ScrW = ScrW
local tonumber = tonumber
local type = type
local unpack = unpack
local ValidEntity = ValidEntity
local Vector = Vector



do
	local hooks = {}
	local created = {}
	local function CallHook(self, name, args)
		if !hooks[name] then return end
		for funcName, _ in pairs(hooks[name]) do
			local func = self[funcName]
			if func then
				local ok, err = pcall(func, self, unpack(args or {}))
				if !ok then
					ErrorNoHalt(err .. "\n")
				elseif err then
					return err

				end
			end
		end
	end
	local function RandomName()
		local random = ""
		for i = 1, math.random(4, 10) do
			local c = math.random(65, 116)
			if c >= 91 && c <= 96 then c = c + 6 end
			random = random .. string.char(c)
		end
		return random
	end
	local function AddHook(self, name, funcName)
		// If we haven't got a hook for this yet, make one with a random name and store it.
		// This is so anti-cheats can't detect by hook name, and so we can remove them later.
		if !created[name] then
			local random = RandomName()
			hook.Add(name, random, function(...) return CallHook(self, name, {...}) end)
			created[name] = random
		end
		
		hooks[name] = hooks[name] or {}
		hooks[name][funcName] = true
	end
	
	local cvarhooks = {}
	local function GetCallbackTable(convar)
		local callbacks = cvars.GetConVarCallbacks(convar)
		if !callbacks then
			cvars.AddChangeCallback(convar, function() end)
			callbacks = cvars.GetConVarCallbacks(convar)
		end
		return callbacks
	end
			
	local function AddCVarHook(self, convar, funcName, ...)
		local hookName = "CVar_" .. convar
		if !cvarhooks[convar] then
			local random = RandomName()
			
			local callbacks = GetCallbackTable(convar)
			callbacks[random] = function(...)
				CallHook(self, hookName, {...})
			end
			
			cvarhooks[convar] = random
		end
		AddHook(self, hookName, funcName)
	end
	
	// Don't let other scripts remove our hooks.
	local oldRemove = hook.Remove
	function hook.Remove(name, unique)
		if created[name] == unique then return end
		oldRemove(name, unique)
	end
	
	// Removes all hooks, useful if reloading the script.
	local function RemoveHooks()
		for hookName, unique in pairs(created) do
			oldRemove(hookName, unique)
		end
		for convar, unique in pairs(cvarhooks) do
			local callbacks = GetCallbackTable(convar)
			callbacks[unique] = nil
		end
	end
	
	// Add copies the script can access.
	BM.AddHook = AddHook
	BM.AddCVarHook = AddCVarHook
	BM.CallHook = CallHook
	BM.RemoveHooks = RemoveHooks
	surface.CreateFont( "arial", 12, 400, true, false, "AimBotSmall" )
	surface.CreateFont( "defaultSmallDropShadow", 24, 500, true, false, "AimBotBig" )
	COLOR_FRIENDLY = Color(0, 255, 0, 255)
	COLOR_ENEMY = Color(51, 14, 234, 255)
	COLOR_DEAD = Color(0, 0, 0, 255)
	COLOR_TRACKING = Color(255, 0, 255, 255)
	
end

concommand.Add("BMpublicaimbot_reload", function()
	BM:CallHook("Shutdown")
	print("Removing hooks...")
	BM:RemoveHooks()
	
	local info = debug.getinfo(1, "S")
	if info && info.short_src then
		print("Reloading (" .. info.short_src .. ")...")
		include(info.short_src)
	else
		print("Cannot find Aimbot file, reload manually.")
	end
end)

// ##################################################
// MetaTables
// ##################################################

local function GetMeta(name)
	return table.Copy(_R[name] or {})
end

local AngM = GetMeta("Angle")
local CmdM = GetMeta("CUserCmd")
local EntM = GetMeta("Entity")
local PlyM = GetMeta("Player")
local VecM = GetMeta("Vector")

// ##################################################
// Settings
// ##################################################

do
	local settings = {}
	local function SettingVar(self, name)
		return (self.SettingPrefix or "") .. string.lower(name)
	end
	
	local function RandomName()
		local random = ""
		for i = 1, math.random(4, 10) do
			local c = math.random(65, 116)
			if c >= 91 && c <= 96 then c = c + 6 end
			random = random .. string.char(c)
		end
		return random
	end
	
	local function SetSetting(name, _, new)
		if !settings[name] then return end
		local info = settings[name]
		
		if info.Type == "number" then
			new = tonumber(new)
		elseif info.Type == "boolean" then
			new = (tonumber(new) or 0) > 0
		end
		
		info.Value = new
	end
	
	local function CreateSetting(self, name, desc, default, misc)
		local cvar = SettingVar(self, name)
		local info = {Name = name, Desc = desc, CVar = cvar, Type = type(default), Value = default}
		
		for k, v in pairs(misc or {}) do
			if !info[k] then info[k] = v end
		end
		
		// Convert default from boolean to number.
		if type(default) == "boolean" then
			default = default and 1 or 0
		end
		
		if !settings[cvar] then
			local tab = cvars.GetConVarCallbacks(cvar)
			if !tab then
				cvars.AddChangeCallback(cvar, function() end)
				tab = cvars.GetConVarCallbacks(cvar)
			end
			
			while true do
				local name = RandomName()
				if !tab[name] then
					tab[name] = SetSetting
					info.Callback = name
					break
				end
			end
		end
		
		settings[cvar] = info
		settings[#settings + 1] = info
		
		// Create the convar.
		CreateClientConVar(cvar, default, (info.Save != false), false)
		SetSetting(cvar, _, GetConVarString(cvar))
	end
	local function GetSetting(self, name)
		local cvar = SettingVar(self, name)
		if !settings[cvar] then return end
		return settings[cvar].Value
	end
	local function Shutdown()
		print("Removing settings callbacks...")
		for _, info in ipairs(settings) do
			if info.CVar && info.Callback then
				local tab = cvars.GetConVarCallbacks(info.CVar)
				if tab then
					tab[info.Callback] = nil
				end
			end
		end
	end
	local function SettingsList()
		return table.Copy(settings)
	end
	local function BuildMenu(self, panel)
		for _, info in ipairs(settings) do
			if info.Show != false then
				if info.MultiChoice then
					local m = panel:MultiChoice(info.Desc or info.CVar, info.CVar)
					for k, v in pairs(info.MultiChoice) do
						m:AddChoice(k, v)
					end
				elseif info.Type == "number" then
					panel:NumSlider(info.Desc or info.CVar, info.CVar, info.Min or -1, info.Max or -1, info.Places or 0)
				elseif info.Type == "boolean" then
					panel:CheckBox(info.Desc or info.CVar, info.CVar)
				elseif info.Type == "string" then
					panel:TextEntry(info.Desc or info.CVar, info.CVar)
				end
			end
		end
	end
	
	BM.SettingPrefix = "BMpublicaimbot_"
	BM.CreateSetting = CreateSetting
	BM.Setting = GetSetting
	BM.SettingsList = SettingsList
	BM.BuildMenu = BuildMenu
	
	BM.SettingsShutdown = Shutdown
	BM:AddHook("Shutdown", "SettingsShutdown")
end


// ##################################################
// Targetting - Positions
// ##################################################

BM.ModelTarget = {}
function BM:SetModelTarget(model, targ)
	self.ModelTarget[model] = targ
end
function BM:BaseTargetPosition(ent)
	// The eye attachment is a lot more stable than bones for players.
	if type(ent) == "Player" then
		local head = EntM["LookupAttachment"](ent, "eyes")
		if head then
			local pos = EntM["GetAttachment"](ent, head)
			if pos then
				return pos.Pos - (AngM["Forward"](pos.Ang) * 2)
			end
		end
	end
	
	// Check if the model has a special target assigned to it.
	local special = self.ModelTarget[string.lower(EntM["GetModel"](ent) or "")]
	if special then
		// It's a string - look for a bone.
		if type(special) == "string" then
			local bone = EntM["LookupBone"](ent, special)
			if bone then
				local pos = EntM["GetBonePosition"](ent, bone)
				if pos then
					return pos
				end
			end
		// It's a Vector - return a relative position.
		elseif type(special) == "Vector" then
			return EntM["LocalToWorld"](ent, special)
		// It's a function - do something fancy!
		elseif type(special) == "function" then
			local pos = pcall(special, ent)
			if pos then return pos end
		end
	end

	// Try and use the head bone, found on all of the player + human models.
	local bone = "ValveBiped.Bip01_Head1"
	local head = EntM["LookupBone"](ent, bone)
	if head then
		local pos = EntM["GetBonePosition"](ent, head)
		if pos then
			return pos
		end
	end

	// Give up and return the center of the entity.
	return EntM["LocalToWorld"](ent, EntM["OBBCenter"](ent))
end
function BM:TargetPosition(ent)
	local targetPos = self:BaseTargetPosition(ent)
	
	local ply = LocalPlayer()
	if ValidEntity(ply) then
		targetPos = self:CallHook("TargetPrediction", {ply, ent, targetPos}) or targetPos
	end
	
	return targetPos
end

BM:SetModelTarget("models/crow.mdl", Vector(0, 0, 5))						// Crow.
BM:SetModelTarget("models/pigeon.mdl", Vector(0, 0, 5)) 					// Pigeon.
BM:SetModelTarget("models/seagull.mdl", Vector(0, 0, 6)) 					// Seagull.
BM:SetModelTarget("models/combine_scanner.mdl", "Scanner.Body") 				// Scanner.
BM:SetModelTarget("models/hunter.mdl", "MiniStrider.body_joint") 			// Hunter.
BM:SetModelTarget("models/combine_turrets/floor_turret.mdl", "Barrel") 		// Turret.
BM:SetModelTarget("models/dog.mdl", "Dog_Model.Eye") 						// Dog.
BM:SetModelTarget("models/vortigaunt.mdl", "ValveBiped.Head") 				// Vortigaunt.
BM:SetModelTarget("models/antlion.mdl", "Antlion.Body_Bone") 					// Antlion.
BM:SetModelTarget("models/antlion_guard.mdl", "Antlion_Guard.Body") 			// Antlion guard.
BM:SetModelTarget("models/antlion_worker.mdl", "Antlion.Head_Bone") 			// Antlion worker.
BM:SetModelTarget("models/zombie/fast_torso.mdl", "ValveBiped.HC_BodyCube") 	// Fast zombie torso.
BM:SetModelTarget("models/zombie/fast.mdl", "ValveBiped.HC_BodyCube") 		// Fast zombie.
BM:SetModelTarget("models/headcrabclassic.mdl", "HeadcrabClassic.SpineControl") // Normal headcrab.
BM:SetModelTarget("models/headcrabblack.mdl", "HCBlack.body") 				// Poison headcrab.
BM:SetModelTarget("models/headcrab.mdl", "HCFast.body") 						// Fast headcrab.
BM:SetModelTarget("models/zombie/poison.mdl", "ValveBiped.Headcrab_Cube1")	 // Poison zombie.
BM:SetModelTarget("models/zombie/classic.mdl", "ValveBiped.HC_Body_Bone")	 // Zombie.
BM:SetModelTarget("models/zombie/classic_torso.mdl", "ValveBiped.HC_Body_Bone") // Zombie torso.
BM:SetModelTarget("models/zombie/zombie_soldier.mdl", "ValveBiped.HC_Body_Bone") // Zombine.
BM:SetModelTarget("models/combine_strider.mdl", "Combine_Strider.Body_Bone") // Strider.
BM:SetModelTarget("models/combine_dropship.mdl", "D_ship.Spine1") 			// Combine dropship.
BM:SetModelTarget("models/combine_helicopter.mdl", "Chopper.Body") 			// Combine helicopter.
BM:SetModelTarget("models/gunship.mdl", "Gunship.Body")						// Combine gunship.
BM:SetModelTarget("models/lamarr.mdl", "HeadcrabClassic.SpineControl")		// Lamarr!
BM:SetModelTarget("models/mortarsynth.mdl", "Root Bone")						// Mortar synth.
BM:SetModelTarget("models/synth.mdl", "Bip02 Spine1")						// Synth.
BM:SetModelTarget("models/vortigaunt_slave.mdl", "ValveBiped.Head")			// Vortigaunt slave.


// ##################################################
// Targetting - General
// ##################################################

BM.NPCDeathSequences = {}
function BM:AddNPCDeathSequence(model, sequence)
	self.NPCDeathSequences = self.NPCDeathSequences or {}
	self.NPCDeathSequences[model] = self.NPCDeathSequences[model] or {}
	if !table.HasValue(self.NPCDeathSequences[model]) then
		table.insert(self.NPCDeathSequences[model], sequence)
	end
end

BM:AddNPCDeathSequence("models/barnacle.mdl", 4)
BM:AddNPCDeathSequence("models/barnacle.mdl", 15)
BM:AddNPCDeathSequence("models/antlion_guard.mdl", 44)
BM:AddNPCDeathSequence("models/hunter.mdl", 124)
BM:AddNPCDeathSequence("models/hunter.mdl", 125)
BM:AddNPCDeathSequence("models/hunter.mdl", 126)
BM:AddNPCDeathSequence("models/hunter.mdl", 127)
BM:AddNPCDeathSequence("models/hunter.mdl", 128)

BM:CreateSetting("friendlyfire", "Target Friend's", false)
function BM:IsValidTarget(ent)
	// We only want players/NPCs.
	local typename = type(ent)
	if typename != "NPC" && typename != "Player" then return false end

	// No invalid entities.
	if !ValidEntity(ent) then return false end

	// Go shoot yourself, emo kid.
	local ply = LocalPlayer()
	if ent == ply then return false end

	if typename == "Player" then
		if !PlyM["Alive"](ent) then return false end // Dead players FTL.
		if !self:Setting("friendlyfire") && PlyM["Team"](ent) == PlyM["Team"](ply) then return false end
		if EntM["GetMoveType"](ent) == MOVETYPE_OBSERVER then return false end // No spectators.
		if EntM["GetMoveType"](ent) == MOVETYPE_NONE then return false end
		//if pl["Team"](ent) == 1001 then return false end
	end

	if typename == "NPC" then
		if EntM["GetMoveType"](ent) == MOVETYPE_NONE then return false end // No dead NPCs.

		// No dying NPCs.
		local model = string.lower(EntM["GetModel"](ent) or "")
		if table.HasValue(self.NPCDeathSequences[model] or {}, EntM["GetSequence"](ent)) then return false end
	end
end

BM:CreateSetting("predictblocked", "Predict blocked (time)", 0.4, {Min = 0, Max = 1})
function BM:BaseBlocked(target, offset)
	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end
	
	// Trace from the players shootpos to the position.
	local shootPos = PlyM["GetShootPos"](ply)
	local targetPos = self:TargetPosition(target)
	
	if offset then targetPos = targetPos + offset end

	local trace = util.TraceLine({start = shootPos, endpos = targetPos, filter = {ply, target}, mask = MASK_SHOT})
	local wrongAim = self:AngleBetween(PlyM["GetAimVector"](ply), VecM["GetNormal"](targetPos - shootPos)) > 2

	// If we hit something, we're "blocked".
	if trace.Hit && trace.Entity != target then
		return true, wrongAim
	end

	// It is not blocked.
	return false, wrongAim
end
function BM:TargetBlocked(target)
	if !target then target = self:GetTarget() end
	if !target then return end
	
	local blocked, wrongAim = self:BaseBlocked(target)
	if self:Setting("predictblocked") > 0 && blocked then
		blocked = self:BaseBlocked(target, EntM["GetVelocity"](target) * self:Setting("predictblocked"))
	end
	return blocked, wrongAim
end
	

function BM:SetTarget(ent)
	if self.Target && !ent then
		self:CallHook("TargetLost")
	elseif !self.Target && ent then
		self:CallHook("TargetGained")
	elseif self.Target && ent && self.Target != ent then
		self:CallHook("TargetChanged")
	end

	self.Target = ent
end
function BM:GetTarget()
	if ValidEntity(self.Target) != false then
		return self.Target
	else
		return false
	end
end

BM:CreateSetting("maxangle", "Max angle", 30, {Min = 1, Max = 360})
BM:CreateSetting("targetblocked", "Don't check LOS", false)
BM:CreateSetting("holdtarget", "Hold targets", false)
function BM:FindTarget()
	if !self:Enabled() then return end

	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end

	local maxAng = self:Setting("maxangle")
	local aimVec, shootPos = PlyM["GetAimVector"](ply), PlyM["GetShootPos"](ply)
	local targetBlocked = self:Setting("targetblocked")

	if self:Setting("holdtarget") then
		local target = self:GetTarget()
		if target then
			local targetPos = self:TargetPosition(target)
			local angle = self:AngleBetween(AngM["Forward"](self:GetView()), VecM["GetNormal"](targetPos - shootPos))
			local blocked = self:TargetBlocked(target)
			if angle <= maxAng && (!blocked || targetBlocked) then return end
		end
	end

	// Filter out targets.
	local targets = ents.GetAll()
	for i, ent in pairs(targets) do
		if self:IsValidTarget(ent) == false then
			targets[i] = nil
		end
	end

	local closestTarget, lowestAngle = _, maxAng
	for _, target in pairs(targets) do
		if targetBlocked || !self:TargetBlocked(target) then
			local targetPos = self:TargetPosition(target)
			local angle = self:AngleBetween(AngM["Forward"](self:GetView()), VecM["GetNormal"](targetPos - shootPos))

			if angle < lowestAngle then
				lowestAngle = angle
				closestTarget = target
			end
		end
	end

	self:SetTarget(closestTarget)
end
BM:AddHook("Think", "FindTarget")


// ##################################################
// Fake view
// ##################################################

BM.View = Angle(0, 0, 0)
function BM:GetView()
	return self.View * 1
end
function BM:KeepView()
	if !self:Enabled() then return end

	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end

	self.View = EntM["EyeAngles"](ply)
end
BM:AddHook("OnToggled", "KeepView")

local sensitivity = 0.022
function BM:RotateView(cmd)
	self.View.p = math.Clamp(self.View.p + (CmdM["GetMouseY"](cmd) * sensitivity), -89, 89)
	self.View.y = math.NormalizeAngle(self.View.y + (CmdM["GetMouseX"](cmd) * sensitivity * -1))
end
BM:AddHook("CreateMove", "RotateView")

BM:CreateSetting("debug", "Debug", false, {Show = false})
function BM:FakeView(ply, origin, angles, FOV)
	if !self:Enabled() && !self.SetAngleTo then return end
	if GetViewEntity() != LocalPlayer() then return end
	if self:Setting("debug") then return end
	
	local base = GAMEMODE:CalcView(ply, origin, self.SetAngleTo or self.View, FOV) or {}
			base.angles = base.angles or (self.AngleTo or self.View)
			base.angles.r = 0 // No crappy screen tilting in ZS.
	return base
end
BM:AddHook("CalcView", "FakeView")


function BM:TargetPrediction(ply, target, targetPos)
	local weap = PlyM["GetActiveWeapon"](ply)
	if ValidEntity(weap) then
		local class = EntM["GetClass"](weap)
		if class == "weapon_crossbow" then
			local dist = VecM["Length"](targetPos - PlyM["GetShootPos"](ply))
			local time = (dist / 3500) + 0.05 // About crossbow bolt speed.
			targetPos = targetPos + (EntM["GetVelocity"](target) * time)
		end
		
		local mul = 0.0075
		//targetPos = targetPos - (e["GetVelocity"](ply) * mul)
	end
	
	return targetPos
end
BM:AddHook("TargetPrediction", "TargetPrediction")

// ##################################################
// Aim
// ##################################################

function BM:SetAngle(ang)
	self.SetAngleTo = ang
end

BM:CreateSetting("smoothspeed", "Smooth Aim Speed (0 To Disable)", 120, {Min = 0, Max = 360})
BM:CreateSetting("snaponfire", "Snap On Fire", true)
BM:CreateSetting("snapgrace", "Snap On Fire Grace", 0.5, {Min = 0, Max = 3, Places = 1})
BM.LastAttack = 0
function BM:SetAimAngles(cmd)
	if !self:Enabled() && !self.SetAngleTo then return end

	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end

	// We're aiming with the view, normally.
	local targetAim = self:GetView()

	// If we have a target, aim at them!
	local target = self:GetTarget()
	if target then
		local targetPos = self:TargetPosition(target)
		targetAim = VecM["Angle"](targetPos - ply:GetShootPos())
	end

	// We're following the view, until we fire.
	if self:Setting("snaponfire") then
		local time = CurTime()
		if PlyM["KeyDown"](ply, IN_ATTACK) || PlyM["KeyDown"](ply, IN_ATTACK2) || self:Setting("autoshoot") != 0 then
			self.LastAttack = time
		end
		if CurTime() - self.LastAttack > self:Setting("snapgrace") then
			targetAim = self:GetView()
		end
	end

	// We want to change to whatever was SetAngle'd.
	if self.SetAngleTo then
		targetAim = self.SetAngleTo
	end

	// Smooth aiming.
	local smooth = self:Setting("smoothspeed")
	if smooth > 0 then
		local current = CmdM["GetViewAngles"](cmd)

		// Approach the target angle.
		current = self:ApproachAngle(current, targetAim, smooth * FrameTime())
		current.r = 0

		// If we're just following the view, we don't need to smooth it.
		if self.RevertingAim then
			local diff = self:NormalizeAngle(current - self:GetView())
			if math.abs(diff.p) < 1 && math.abs(diff.y) < 1 then self.RevertingAim = false end
		elseif targetAim == self:GetView() then
			current = targetAim
		end

		// Check if the angles are the same...
		if self.SetAngleTo then
			local diff = self:NormalizeAngle(current - self.SetAngleTo)
			if math.abs(diff.p) < 1 && math.abs(diff.y) < 1 then self.SetAngleTo = nil end
		end

		aim = current
	else
		aim = targetAim
		self.SetAngleTo = nil
	end

	// Set the angles.
	CmdM["SetViewAngles"](cmd, aim)
	local sensitivity = 0.22
	local diff = aim - CmdM["GetViewAngles"](cmd)
	CmdM["SetMouseX"](cmd, diff.y / sensitivity)
	CmdM["SetMouseY"](cmd, diff.p / sensitivity)
	

	// Change the players movement to be relative to their view instead of their aim.
	local move = Vector(CmdM["GetForwardMove"](cmd), CmdM["GetSideMove"](cmd), 0)
	local norm = VecM["GetNormal"](move)
	local set = AngM["Forward"](VecM["Angle"](norm) + (aim - self:GetView())) * VecM["Length"](move)
		CmdM["SetForwardMove"](cmd, set.x)
		CmdM["SetSideMove"](cmd, set.y)
end
BM:AddHook("CreateMove", "SetAimAngles")

function BM:RevertAim()
	self.RevertingAim = true
end
BM:AddHook("TargetLost", "RevertAim")
function BM:StopRevertAim()
	self.RevertingAim = false
end
BM:AddHook("TargetGained", "RevertAim")

// When we turn off the bot, we want our aim to go back to our view.
function BM:ViewToAim()
	if self:Enabled() then return end
	self:SetAngle(self:GetView())
end
BM:AddHook("OnToggled", "ViewToAim")


// ##################################################
// HUD
// ##################################################

BM:CreateSetting("KillBox", "KillBox Size (0 To Disable)", 90, {Min = 0, Max = 120})
function BM:DrawTarget()
	if !self:Enabled() then return end
	
		if !self:Enabled() then return end



	local target = self:GetTarget()
	if !target then return end

	local size = self:Setting("KillBox")
	if size <= 0 then return end

	// Change colour on the block status.
	local blocked, aimOff = self:TargetBlocked()
	if blocked then
		surface.SetDrawColor(255, 0, 0, 255) // Red.
	elseif aimOff then
		surface.SetDrawColor(48, 48, 48, 255) // Yellow.
	else
		surface.SetDrawColor(51, 14, 234, 255) // Green.
	end

	// Get the onscreen coordinates for the target.
	local pos = self:TargetPosition(target)

	local screen = VecM["ToScreen"](pos)
	local x, y = screen.x, screen.y

	// Work out sizes.
	local a, b = size / 2, size / 6

	// Top left.
	surface.DrawLine(x - a, y - a, x - b, y - a)
	surface.DrawLine(x - a, y - a, x - a, y - b)

	// Bottom right.
	surface.DrawLine(x + a, y + a, x + b, y + a)
	surface.DrawLine(x + a, y + a, x + a, y + b)

	// Top right.
	surface.DrawLine(x + a, y - a, x + b, y - a)
	surface.DrawLine(x + a, y - a, x + a, y - b)

	// Bottom left.
	surface.DrawLine(x - a, y + a, x - b, y + a)
	surface.DrawLine(x - a, y + a, x - a, y + b)
end
BM:AddHook("HUDPaint", "DrawTarget")


BM.ScreenMaxAngle = {
	Length = 0,
	FOV = 0,
	MaxAngle = 0
}
BM:CreateSetting("draw_maxangle", "Draw Ingame Visual's", true)
function BM:DrawMaxAngle()
	if !self:Enabled() then return end

	// Check that we want to be drawing this...
	local show = BM:Setting("draw_maxangle")
	if !show then return end

	// We need a player for this to work...
	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end

	local info = self.ScreenMaxAngle
	local maxang = BM:Setting("maxangle")
	
	local fov = PlyM["GetFOV"](ply)
	if GetViewEntity() == ply && (maxang != info.MaxAngle || fov != info.FOV) then
		local view = self:GetView()
			view.p = view.p + maxang

		local screen = (PlyM["GetShootPos"](ply) + (AngM["Forward"](view) * 100))
		screen = VecM["ToScreen"](screen)

		info.Length = math.abs((ScrH() / 2) - screen.y)

		info.MaxAngle = maxang
		info.FOV = fov
	end

	local length = info.Length

	local cx, cy = ScrW() / 2, ScrH() / 2
	for x = -1, 1 do
		for y = -1, 1 do
			if x != 0 || y != 0 then
				local add = VecM["GetNormal"](Vector(x, y, 0)) * length
				surface.SetDrawColor(255, 255, 255, 255)
				surface.DrawRect((cx + add.x) - 2, (cy + add.y) - 2, 5, 5)
				surface.SetDrawColor(51, 14, 234, 255)
				surface.DrawRect((cx + add.x) - 1, (cy + add.y) - 1, 3, 3)
				draw.SimpleText("[GzF] Aimbot", "AimBotBig", ScrW() * 0.5, ScrH() * 0.01, COLOR_ENEMY, TEXT_ALIGN_CENTER)
				local x = ScrW() / 2.0
				local y = ScrH() / 2.0 
	
				surface.SetDrawColor( 51, 14, 234, 255 )
	
				surface.DrawLine( x, y, x+20, y )
				surface.DrawLine( x, y, x-20, y )
				surface.DrawLine( x, y, x, y+20 )
				surface.DrawLine( x, y, x, y-20 )
			end
		end
	end
	
end
BM:AddHook("HUDPaint", "DrawMaxAngle")

// ##################################################
// Auto-shoot
// ##################################################

BM.AttackDown = false
function BM:SetShooting(bool)
	if self.AttackDown == bool then return end
	self.AttackDown = bool

	local pre = {[true] = "+", [false] = "-"}
	RunConsoleCommand(pre[bool] .. "attack")
end

BM.NextShot = 0
BM:CreateSetting("autoshoot", "Max Auto-Shoot Distance (0 To Disable)", 0, {Min = 0, Max = 16384})
function BM:Shoot()
	if !self:Enabled() then
		self:SetShooting(false)
		return
	end

	// Get the maximum distance.
	local maxDist = self:Setting("autoshoot")
	if maxDist == 0 then return end

	// Check we've got something to shoot at...
	local target = self:GetTarget()
	if !target then return end
	
	// Don't shoot until we can hit, you idiot!
	local blocked, wrongAim = self:TargetBlocked(target)
	if blocked || wrongAim then return end

	// We're gonna need the player object in a second.
	local ply = LocalPlayer()
	if !ValidEntity(ply) then return end
	
	// Check we're within our maximum distance.
	local targetPos = self:TargetPosition(target)
	local distance = VecM["Length"](targetPos - ply:GetShootPos())
	if distance > maxDist && maxDist != -1 then return end

	// Check if it's time to shoot yet.
	if CurTime() < self.NextShot then return end

	// Check we got our weapon.
	local weap = PlyM["GetActiveWeapon"](ply)
	if !ValidEntity(weap) then return end

	// Shoot!
	self:SetShooting(true)
	// If we're semi-auto, we want to stop holding down fire.
	if self:IsSemiAuto(weap) then
		timer.Simple(0.05, function() self:SetShooting(false) end)
	end

	// Set the next time to shoot.
	self.NextShot = CurTime() + 0.1
end
BM:AddHook("Think", "Shoot")

// When we lose our target we stop shooting.
function BM:StopShooting()
	self:SetShooting(false)
end
BM:AddHook("TargetLost", "StopShooting")

// ##################################################
// Toggle
// ##################################################

BM.IsEnabled = false
function BM:Enabled() return self.IsEnabled end

function BM:SetEnabled(bool)
	if self.IsEnabled == bool then return end
	self.IsEnabled = bool

	local message = {[true] = "ON", [false] = "OFF"}
	print("[GzF] Aimbot " .. message[self.IsEnabled])

	local e = {[true] = "1", [false] = "0"}
	RunConsoleCommand("BMpublicaimbot_enabled", e[self.IsEnabled])

	self:CallHook("OnToggled")
end

function BM:Toggle()
	self:SetEnabled(!self:Enabled())
end
concommand.Add("BMpublicaimbot_toggle", function() BM:Toggle() end)

BM:CreateSetting("enabled", "Enabled ", false, {Save = false})
function BM:ConVarEnabled(_, old, val)
	if old == val then return end
	val = tonumber(val) or 0
	self:SetEnabled(val > 0)
end
BM:AddCVarHook("BMpublicaimbot_enabled", "ConVarEnabled")

concommand.Add("+BMaimbot", function() BM:SetEnabled(true) end)
concommand.Add("-BMaimbot", function() BM:SetEnabled(false) end)

// ##################################################
// Menu
// ##################################################

function BM:OpenMenu()
	local w, h = ScrW() / 3, ScrH() / 2

	local menu = vgui.Create("DFrame")
	menu:SetTitle("[GzF] Aimbot")
	menu:SetSize(w, h)
	menu:Center()
	menu:MakePopup()

	local scroll = vgui.Create("DPanelList", menu)
	scroll:SetPos(5, 25)
	scroll:SetSize(w - 10, h - 30)
	scroll:EnableVerticalScrollbar()

	local form = vgui.Create("DForm", menu)
	form:SetName("")
	form.Paint = function() end
	scroll:AddItem(form)

	self:BuildMenu(form)

	if BM.Menu then BM.Menu:Remove() end
	BM.Menu = menu
end
concommand.Add("BMpublicaimbot_menu", function() BM:OpenMenu() end)

function BM:RegisterMenu()
	spawnmenu.AddToolMenuOption("[GzF] Aimbot 1.0.8", "[GzF] Aimbot", "AimBot", "Settings", "", "", function(p) self:BuildMenu(p) end)
end
BM:AddHook("PopulateToolMenu", "RegisterMenu")

// ##################################################
// Useful functions
// ##################################################

	

function BM:AngleBetween(a, b)
	return math.deg(math.acos(VecM["Dot"](a, b)))
end

function BM:NormalizeAngle(ang)
	return Angle(math.NormalizeAngle(ang.p), math.NormalizeAngle(ang.y), math.NormalizeAngle(ang.r))
end

function BM:ApproachAngle(start, target, add)
	local diff = self:NormalizeAngle(target - start)

	local vec = Vector(diff.p, diff.y, diff.r)
	local len = VecM["Length"](vec)
	vec = VecM["GetNormal"](vec) * math.min(add, len)

	return start + Angle(vec.x, vec.y, vec.z)
end

local notAuto = {"weapon_pistol", "weapon_rpg", "weapon_357", "weapon_crossbow"}
function BM:IsSemiAuto(weap)
	if !ValidEntity(weap) then return end
	return (weap.Primary && !weap.Primary.Automatic) || table.HasValue(notAuto, EntM["GetClass"](weap))
end
